Structured financial reporting is latest and greatest method of exchanging financial data and reports. The use of XBRL is expanding allover the world, and it is becoming the standard method for exchanging structured financial reports.
The objective of this document is to provide the reader with a basic understanding of what XBRL is, what it does, and how it is implemented.
Parts of this material depends heavily, and refers to the XBRL Taxonomy Development Handbook published by XBRL US and publicly available on the their website. As mentioned in the preface of the handbook, it was created as a guide for creating XBRL taxonomies based on XBRL US experience, which makes it a very valuable resource for anyone or organization interested in implementation of XBRL.
This material should provide:
To explain the most basic concept of XBRL we need to take a trip back in time, to the ancient Egyptian writings.
[Image by Osama Shukir Muhammed Amin FRCP(Glasg), CC BY-SA 4.0 https://creativecommons.org/licenses/by-sa/4.0, via Wikimedia Commons]
In the image above we see some writing encapsulated in an oval shape called “Cartouche”, according to the common understanding, this means that the encapsulated writing represents a royal name. The ancient Egyptians choose this method to draw the attention to the information by marking or “tagging” this important information by the oval shape.
XBRL does the same thing, it is tagging important financial information included in a report, in the case of XBRL, this tagging has consequences when the information is processed by a computer.
XBRL stands for eXtensible Business Reporting Language, and it is usually described in terms of what it does, in brief, XBRL provides standards for storing and electronic communication of financial information enabling efficient processing, storage, retrieval, analysis and comparison of the information.
The increase in size of data and regulatory requirements derives the need for more efficient and structured methods to handle all this data and make convert it into a resource rather than a burden.
The XBRL Taxonomy Development Handbook in page 6 lists successful implementation of XBRL around the worlds, that includes:
United States: Stock exchange commission (SEC), and Financial Depository Insurance Corporation (FDIC) with total reporting entities of over 17,500
United Kingdom: Her Majesty’s Revenues & Customs (HMRC), and Companies House with reporting entities of over 2 million
Spain: Business Registrar, Banking Regulator, Securities Regulation, Accounting Oversight and State Federal Comptroller with reporting entities of over 800,000
Others: Europe (European Single Electronic Format ESEF), India, Singapore, South Korea, Italy, Peru, World bank and many others
Governments and government agencies allover the world are using XBRL, countries like Netherlands and Australia implemented Standard Business Reporting (SBR) programs which are programs designed to reduce regulatory burden for businesses and relys heavily on XBRL.
Currently XBRL international website lists more than 20 XBRL jurisdictions (a jurisdiction is a local representative for XBRL acting as the primary liaison to national government, technology firms and business communities)
{fig:changes_in_data_collection}How we collect DataXXXX
The methods and processes of financial data collection evolved over time, we started with paper based submissions, then computer discs, then electronic submissions through web based portals. XBRL is the next new thing in this evolution, and it adds value in a lot of ways such as:
And many more benefits relating to the quality and richness of that data that we will look into later.
As mentioned, XBRL provides for structured contextually rich machine readable data, which allows for automation, and that address most of the main data issues in general, for regulators and for issuers.
General Issues:
Regulator Issues:
Issuer Issues:
Why XBRL? In short, it addresses most current issues relating to exchange of financial data, it is widely used allover the world, and it is simply the next step in the evolution of financial data exchange systems.
The Specifications
Technically XBRL is based on XML (eXtensible Markup Language), it can be said that XBRL is an XML extension optimized to deal with business information. In other words, XBRL does what it does by being based on XML.
XBRL is a set of specifications developed and maintained by XBRL International. The base XBRL specification (now version 2.1) is stable since 2003, with additional specifications being added to augment it such as XBRL Dimensions.
XBRL specification are freely available without licensing, note that this doesn’t apply for XBRL enabled software which might have licensing fees.
Data Model
XBRL specifications are tools that enables the definition of dictionaries, data models and rules called XBRL Taxonomies, also XBRL specifications provide the tools to create structured financial reports based on XBRL Taxonomies, these financial reports are called XBRL Instances.
So we can say that XBRL is the set of tools used to create data models and structures that are the basis for structured financial reporting.
Communication Language
The purpose of XBRL is to enable exchange of structured financial data between systems, some times the term “transport model” is used to refer to XBRL.
“A Transport Model serves as an organizational structure when moving data from a source to a consumer”
Understand XBRL and how it does what it does, start with XML, in the next section we will go through some of XML concepts that are relevant to understanding XBRL.
Markup languages in general tags the content of a file or a document in a way that makes it machine readable, i.e. when processed by a computer, the tags tell the computer what to do with the content.
Markup languages has different purposes, for example Hyper Text Markup Language (“HTML”) tags tell the computer how to display the content, while few of the main purposes of XML is to store, organize and transport content between systems.
Markup languages are usually system independent, for example all systems have tools to read and parse XML, in other words, an XML file created in a Windows system can be read an parsed by a Linux based system.
As mentioned XML is a markup language, and it is a set of specifications, rules and tools for describing, storing, and transporting data between systems.
Assume that we want to encode a table of invoices into XML, a fragment of that XML might look as follows:
<table>
<invoice CustomerName="abc"
InvoiceNum="101">589.91
</invoice>
</table>XML document is composed of elements, each element starts with an opening tag and ends with a closing tag, there can be values or other elements within the opening and closing tags. The XML structure is in the form of a tree, having a root element containing all other elements.
<table> and </table> in the above XML fragment are the opening and closing tags of the root element called table. In the above fragment, the root element has only one child element called invoice. The invoice opening tag contains other information in the form of key, value pairs customerName="abc", invoiceNum=101, these are called attributes, which attaches more information about the element. finally we have a value 589.91 between the invoice opening and closing tag, in this case representing the invoice amount.
To be usable, XML must be well formed XML, a well formed XML has the following:
For more about XML well formedness see W3Schools XML Tutorial
Let’s assume we have a table of invoices that we need to store in XML format and send over to another computer, first let’s construct the table:
# Generate a table, same as previous test but 50 rows
set.seed(42)
# Number of rows in the table
table_rows <- 10
# Customer names
customer_names <- c("abc", "mno","xyz")
# Data frame
tbl_1 <- data.frame(
CustomerName = sample(customer_names, table_rows, replace = T),
InvoiceNum = sort(sample(100:999, table_rows)),
InvoiceDate = sort(sample(seq(as.Date('2000-01-01'),
as.Date('2000-12-31'),
by="day"), table_rows)
),
InvoiceCurrency = rep("CU",table_rows),
InvoiceAmt = round(runif(table_rows, min = 100, max = 1000),2), stringsAsFactors = F)
# Display first few rows of the data.frame
head(tbl_1)## CustomerName InvoiceNum InvoiceDate InvoiceCurrency InvoiceAmt
## 1 abc 264 2000-01-05 CU 650.60
## 2 abc 396 2000-01-24 CU 441.60
## 3 abc 455 2000-04-18 CU 492.19
## 4 abc 509 2000-07-30 CU 133.69
## 5 mno 631 2000-09-15 CU 976.19
## 6 mno 700 2000-10-09 CU 488.58
Now let’s convert that table to XML format:
# This code converts the invoices table to an XML document
# and saves it to file
# Create xml root element
xml_root <- xml2::xml_new_root('table')
# Attach each row of the table as an <invoice> element
for(r in asplit(tbl_1,1)) {
nd <- xml2::xml_add_child(xml_root, 'invoice')
for(r_n in names(r)){
xml2::xml_add_child(.x=nd, .value = r_n, r[[r_n]] )
}
}
# Write the xml document to file
xml_out_tbl_1 <- here::here('xml_files','xml_out.xml')
invisible(xml2::write_xml(xml_root, xml_out_tbl_1))The resulting XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<table>
<invoice>
<CustomerName>abc</CustomerName>
<InvoiceNum>264</InvoiceNum>
<InvoiceDate>2000-01-05</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>650.60</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>abc</CustomerName>
<InvoiceNum>396</InvoiceNum>
<InvoiceDate>2000-01-24</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>441.60</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>abc</CustomerName>
<InvoiceNum>455</InvoiceNum>
<InvoiceDate>2000-04-18</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>492.19</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>abc</CustomerName>
<InvoiceNum>509</InvoiceNum>
<InvoiceDate>2000-07-30</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>133.69</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>mno</CustomerName>
<InvoiceNum>631</InvoiceNum>
<InvoiceDate>2000-09-15</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>976.19</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>mno</CustomerName>
<InvoiceNum>700</InvoiceNum>
<InvoiceDate>2000-10-09</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>488.58</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>mno</CustomerName>
<InvoiceNum>721</InvoiceNum>
<InvoiceDate>2000-10-24</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>961.82</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>abc</CustomerName>
<InvoiceNum>978</InvoiceNum>
<InvoiceDate>2000-11-09</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>898.98</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>xyz</CustomerName>
<InvoiceNum>981</InvoiceNum>
<InvoiceDate>2000-12-13</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>675.98</InvoiceAmt>
</invoice>
<invoice>
<CustomerName>xyz</CustomerName>
<InvoiceNum>998</InvoiceNum>
<InvoiceDate>2000-12-25</InvoiceDate>
<InvoiceCurrency>CU</InvoiceCurrency>
<InvoiceAmt>973.87</InvoiceAmt>
</invoice>
</table>
Examining the resulting XML file, each <invoice> element has 5 child elements representing information about each invoice, with each of those child elements storing the information as its value. Now if the focus of this table/report is on the invoice amount invoiceAmt, then it might be better to have the invoice amount information as the only value, and everything else might be better represented as an attribute. Attributes usually give additional contextual information about the element and its value, we may call those attributes aspects or even dimensions. So let’s try to rewrite the XML in a different way to reflect this:
# Re-write the xml file with attributes
# create root element for the new XML
xml_root_2 <- xml2::xml_new_root('table')
# define children with attributes
for(r in asplit(tbl_1,1)) {
nd <- xml2::xml_add_child(xml_root_2, 'invoice', r[[length(r)]])
for(r_n in names(r)){
xml2::xml_attrs(nd) <- r[-length(r)]
}
}
# Write the xml document to file
xml_out_tbl_2 <- here::here('xml_files','xml_out_2.xml')
invisible(xml2::write_xml(xml_root_2, xml_out_tbl_2))The resulting New XML file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<table>
<invoice CustomerName="abc" InvoiceNum="264" InvoiceDate="2000-01-05" InvoiceCurrency="CU">650.60</invoice>
<invoice CustomerName="abc" InvoiceNum="396" InvoiceDate="2000-01-24" InvoiceCurrency="CU">441.60</invoice>
<invoice CustomerName="abc" InvoiceNum="455" InvoiceDate="2000-04-18" InvoiceCurrency="CU">492.19</invoice>
<invoice CustomerName="abc" InvoiceNum="509" InvoiceDate="2000-07-30" InvoiceCurrency="CU">133.69</invoice>
<invoice CustomerName="mno" InvoiceNum="631" InvoiceDate="2000-09-15" InvoiceCurrency="CU">976.19</invoice>
<invoice CustomerName="mno" InvoiceNum="700" InvoiceDate="2000-10-09" InvoiceCurrency="CU">488.58</invoice>
<invoice CustomerName="mno" InvoiceNum="721" InvoiceDate="2000-10-24" InvoiceCurrency="CU">961.82</invoice>
<invoice CustomerName="abc" InvoiceNum="978" InvoiceDate="2000-11-09" InvoiceCurrency="CU">898.98</invoice>
<invoice CustomerName="xyz" InvoiceNum="981" InvoiceDate="2000-12-13" InvoiceCurrency="CU">675.98</invoice>
<invoice CustomerName="xyz" InvoiceNum="998" InvoiceDate="2000-12-25" InvoiceCurrency="CU">973.87</invoice>
</table>
Now that we have modeled our information in an acceptable form, we can try to re-construct the table from the XML, here I am using Rscript xml2 library to do that, but it can be done on any system that is capable of parsing XML files:
# Read xml file
xml_tbl <- xml2::read_xml(xml_out_tbl_2)
# find all invoice elements
invoices <- xml2::xml_find_all(xml_tbl, './/invoice')
values <- xml2::xml_find_all(xml_tbl, './/invoice/text()') %>% xml2::as_list() %>% unlist()
# extract invoice attributes and values from all elements and convert to a dataframe
xml_to_tbl <- xml2::xml_attrs(invoices) %>% bind_rows() %>%
mutate(InvoiceAmt= as.double(values)) %>% as.data.frame()
# Correct data types
xml_to_tbl$InvoiceNum <- as.integer(xml_to_tbl$InvoiceNum)
xml_to_tbl$InvoiceDate <- as.Date(xml_to_tbl$InvoiceDate)
head(xml_to_tbl)
# Compare result of conversion to original table
paste("Matches Original: ", all_equal(xml_to_tbl, tbl_1)) # Should return TRUE## CustomerName InvoiceNum InvoiceDate InvoiceCurrency InvoiceAmt
## 1 abc 264 2000-01-05 CU 650.60
## 2 abc 396 2000-01-24 CU 441.60
## 3 abc 455 2000-04-18 CU 492.19
## 4 abc 509 2000-07-30 CU 133.69
## 5 mno 631 2000-09-15 CU 976.19
## 6 mno 700 2000-10-09 CU 488.58
## [1] "Matches Original: TRUE"
As mentioned, XML is used to transport information between systems, and that an XML document is created, the next step will be to send to the destination system. But an important question arises, how do we make sure that the destination/receiving system is able to handle and verify the information in our document correctly? For example, the root element in the example document is called table, what should be expected to be included in a table element? Is it a table of invoices, or is it a table a piece of furniture?
To address the above questions, XML has mechanisms whereby elements in an XML document can be described and verified, these is mechanisms mainly depend on schema and namespaces.
Schema Is a component of XML (W3C recommendation) used to describe and validate elements in an XML document. Schema can be described as the blueprint of vocabulary used, what and how data is stored in an XML file, there are different schema languages such as Document Type Definitions (DTDs), Relax-NG, Schematron and W3C XSD (XML Schema Definitions). The focus will be on XSD as this is the Schema language used in XBRL.
Namespaces Is a component of XML (W3C recommendation) used for providing uniquely named elements and attributes in an XML document. XML document may contain elements from multiple vocabularies (schema), namespaces help in uniquely identifying elements from different vocabularies having identical names. A namespace takes the form of a URI, for example http://mynamespace.com/1/1. A namespace prefix can be declared in an XML document to refer to specific namespace using xmlns attribute, for example xmlns:myns=http://mynamespace.com/1/1.
Following with the invoices table example, a schema was created for this report (using any schema creation software), the schema insures the following:
http://myproject.com/test2/1 was given to refer to the vocabulary of the schematable and contains one or more invoice elementInvoiceNum of data type positive integerInvoiceDate of data type dateInvoiceCurrency a string that can be either “CU” or “CX”CustomerName of data type stringSchema file is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:inv="http://myproject.com/test2/1"
targetNamespace="http://myproject.com/test2/1"
elementFormDefault="qualified"
>
<xs:simpleType name="currType">
<xs:annotation>
<xs:documentation>Currency type selection either "CU" or "CX"
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:enumeration value="CU" />
<xs:enumeration value="CX" />
</xs:restriction>
</xs:simpleType>
<xs:attributeGroup name="invoiceGrp">
<xs:annotation>
<xs:documentation>Type defining the invoice required information
</xs:documentation>
</xs:annotation>
<xs:attribute name="InvoiceNum" type="xs:unsignedShort"
use="required" />
<xs:attribute name="InvoiceDate" type="xs:date"
use="required" />
<xs:attribute name="InvoiceCurrency" type="inv:currType"
use="required" />
<xs:attribute name="CustomerName" type="xs:string"
use="required" />
</xs:attributeGroup>
<xs:simpleType name="positive_decimalType">
<xs:annotation>
<xs:documentation>Restriction on invoice amount to be always a
positive number.</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="invoiceType">
<xs:annotation>
<xs:documentation>Invoice type based using declared positive decimal
type and invoice attributes group.</xs:documentation>
</xs:annotation>
<xs:simpleContent>
<xs:extension base="inv:positive_decimalType">
<xs:attributeGroup ref="inv:invoiceGrp" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:element name="table">
<xs:annotation>
<xs:documentation>Defines root node using declared invoiceType.
</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="invoice"
type="inv:invoiceType" minOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Now we need to change our XML file to reference the schema, that is done using the xmlns attribute and giving it a namespace prefix of ‘inv’, and providing the location of the schema file using xs:schemaLocation attribute, not that the later attribute is from xs=http://www.w3.org/2001/XMLSchema-instance namespace. The new file with the schema reference is named xml_out_2_schema.xml and and the relevant part of it looks as follows:
<inv:table xmlns:inv="http://myproject.com/test2/1"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://myproject.com/test2/1 example_2_schema2.xsd">Validation with no errors Before processing the XML file, the receiving computer will always validate the XML file against the referenced schema, we can do that here using Rscript xml2::xml_validate() function as follows:
# Read XML instance and schema
inst <- xml2::read_xml(here::here("xml_files","xml_out_2_schema.xml"))
schema <- xml2::read_xml(here::here("xml_files","example_2_schema2.xsd"))
# Validate XML instance against the schema
xml2::xml_validate(inst,schema)## [1] TRUE
## attr(,"errors")
## character(0)
Validating the first file returns TRUE with 0 errors, meaning that the file is valid according to the schema.
Validation with Errors
Now let’s change the file and test if the validation will detect the errors. We create a new file called xml_out_2_schema_errors.xml, and we change it to as follows:
CustomerName attribute -> test missing attributes are detectedInvoiceNum value to string ix-> test inconsistent attribute datatype is detectedInvoiceCurrency value to XZ -> test only valid currency choices are allowed133.69 to -133.69 -> test if only positive invoice amount values are allowed.Then we run the validation again on the modified file, we should get an error this time:
# Read XML instance and schema",
inst_err <- xml2::read_xml(here::here("xml_files","xml_out_2_schema_errors.xml"))
schema <- xml2::read_xml(here::here("xml_files","example_2_schema2.xsd"))
# Validate XML instance against the schema
xml2::xml_validate(inst_err,schema)## [1] FALSE
## attr(,"errors")
## [1] "Element '{http://myproject.com/test2/1}invoice': The attribute 'CustomerName' is required but missing."
## [2] "Element '{http://myproject.com/test2/1}invoice', attribute 'InvoiceNum': 'ix' is not a valid value of the atomic type 'xs:unsignedShort'."
## [3] "Element '{http://myproject.com/test2/1}invoice', attribute 'InvoiceCurrency': [facet 'enumeration'] The value 'XZ' is not an element of the set {'CU', 'CX'}."
## [4] "Element '{http://myproject.com/test2/1}invoice', attribute 'InvoiceCurrency': 'XZ' is not a valid value of the atomic type '{http://myproject.com/test2/1}currType'."
## [5] "Element '{http://myproject.com/test2/1}invoice': [facet 'minInclusive'] The value '-133.69' is less than the minimum value allowed ('0')."
## [6] "Element '{http://myproject.com/test2/1}invoice': '-133.69' is not a valid value of the atomic type '{http://myproject.com/test2/1}positive_decimalType'."
As shown above, a simple XML validator (xml2) detected all the errors and reported them.
Another constructs that XML provides is XLink and XPointer, these constructs can be used to link other constructs within XML or link to external resources.
XLink is a W3C recommendation some what like hyperlink in HTML:
XML Linking Language (XLink) Version 1.1, which allows elements to be inserted into XML documents in order to create and describe links between resources. It uses XML syntax to create structures that can describe links similar to the simple unidirectional hyperlinks of today’s HTML, as well as more sophisticated links.
XPointer is a W3C recommendation is a contstruct that allows for allocating specific fragment within XML.
It is important to keep in mind that XML and its constructs are just instructions, they do nothing other than transporting information. For the XLink and XPointer constructs to have an effect, it needs to be processed by an XML processor.
XLink and XPointer Example
There is no browser support for XML XLink, but the best way to simulate it and understand the idea of XLink and XPointer is to use hyperlinks, Click this link: https://www.w3.org/TR/xlink11/#abstract
We should be taken to the XLink recommendation on the W3C website at the location of Abstract, what happened here is that the browser recognized the hyperlink (XLink) and then the locator given after the # symbol (XPointer) and executed the instructions, and in this case the instructions was to open the website at the specificed location, so we were able to link form this page to another external page. In XML XLink works the same way, it links elements to other elements, or external resources with specific instructions based on the attributes and types of links used.
XBRL uses XLinks extensively to link its components, and we will go into this in details later, for now let’s just be familiar with the concept of XLink and XPointer.
XML as language and standards that provides for:
* Flexibility in data modeling
* Mechanisms for creating vocabularies (dictionaries)
* Mechanisms to validate XML content
* Mechanisms to link internal and external components
In addition to the above, XML is a stable and widely used language, and all that made XML suitable for the objectives of XBRL.
Now that we are familiar with XML we can look into the components of XBRL in more details. In this section we look at the components of XBRL, specifications, basic terminology, basic concepts and how it represents financial Data.
XBRL Components
As explained previously XBRL is an extension of XML, basically XBRL international used XML to define XBRL constructs and elements resulting in XBRL specifications.
As of date of date of this document, the relevant current XBRL specifications recommendations are as follows: